package scene;


import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.universe.SimpleUniverse;

import interaction.Pick;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.media.j3d.*;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import objects.Clock;
import objects.Lights;
import objects.Wall;
import start.Config;

/**
 * Szene, in der alle Objekte dargestellt werden.
 * 
 * @version 1.0
 * @author Imanuel Rohlfing
 * @author Dennis Wittkoetter
 */
public class Scene implements KeyListener{
    private Canvas3D canvas;
    private SimpleUniverse sUniverse;
    private BranchGroup baseBG;
    private TransformGroup baseTG;
    private Clock clock;
    private int keyPressedCounter;
    private boolean clockWasRunning;
    
    /**
     * Konstruiert die Szene.
     */
    public Scene() {
        this.canvas      = new Canvas3D( SimpleUniverse.getPreferredConfiguration() );
        canvas.addKeyListener(this);
        
        // das SimpleUniverse erzeugen
        this.sUniverse   = new SimpleUniverse( canvas );
        sUniverse.getViewingPlatform().setNominalViewingTransform();
        
        // oberste BranchGroup erzeugen und Rechte konfigurieren
        this.baseBG      = new BranchGroup();
        baseBG.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
        baseBG.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
        baseBG.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        baseBG.setCapability(BranchGroup.ALLOW_BOUNDS_READ);
        
        // oberste TransformGroup erzeugen und Rechte konfigurieren
        this.baseTG      = new TransformGroup(); 
        baseTG.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
        baseTG.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
		baseTG.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
		baseTG.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		baseTG.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        // Ambient-Objekte erzeugen und der BranchGroup hinzufuegen
        baseTG.addChild( new Wall().get() );
        baseTG.addChild( new Lights().get() );
        baseTG.addChild( new SceneBackground().get() );

        // die Uhr erzeugen und der BranchGroup hinzufuegen
        clock = new Clock(Config.wrl_name);
        baseTG.addChild( clock.getBranchGroup() );
        baseTG.setTransform( getView(0) );
        
        // TransformGroup der BranchGroup hinzufuegen
        baseBG.addChild(baseTG);
        
        // Picking initialisieren
        Pick simplePick = new Pick(canvas, baseBG, clock, new BoundingSphere());
        baseBG.addChild(simplePick);
        
        baseBG.compile();
        sUniverse.addBranchGraph( baseBG );

        // Interaktion mit der Maus
        OrbitBehavior ob = new OrbitBehavior(canvas);
        ob.setSchedulingBounds(new BoundingSphere( new Point3d(0.0, 0.0, 0.0), Double.MAX_VALUE) );
        ob.setRotYFactor(Config.rotY_factor);
        ob.setReverseRotate(true);
        ob.setReverseTranslate(true);
        ob.setZoomFactor(Config.zoom_factor);
        sUniverse.getViewingPlatform().setViewPlatformBehavior(ob);	
        
        // sonstige Variablenwerte definieren
        this.keyPressedCounter = 0;
        clockWasRunning = false;
    }
    
    @Override
    public void keyTyped(KeyEvent e) {}
        
    
    @Override
    public void keyReleased(KeyEvent e) {
    	keyPressedCounter = 0;
    	
    	if (clockWasRunning) {
    		clock.start();
    		clockWasRunning = false;
    	}
    }
    
    @Override
    public void keyPressed(KeyEvent e) {
    	keyPressedCounter++;	//Anzahl der erfassten Signale pro Tastendruck erfassen
    	
    	if (Config.debug)
    		System.out.println( e.getKeyCode() + "-Taste gedrueckt " );
    	   	       
        //Tastendruck auswerten        
        switch( e.getKeyCode() ) {
        case 32:	//Leertaste
        	if (clock.isRunning())
        		clock.stop();
        	else
        		clock.start();
        	break;
        case 37:	//Pfeil zurueck
        	spinClockBackward();    
        	break;
        case 39:  	//Pfeil vor
        	spinClockForward();
        	break;
        case 49: 	//1
        	baseTG.setTransform( getView(0) ); 
        	break;
        case 50: 	//2
        	baseTG.setTransform( getView(1) ); 
        	break;
        case 84:	//t
        	turboMode();
        }
    }
    
    /**
     * Setzt die vorkonfigurierte Ansicht auf die Objekte in der Szene.
     * Ansicht 1: Frontal von vorn
     * Ansicht 2: Schraeg oben von vorn
     * 
     * @param i	1 oder 2 fuer die entsprechende Ansicht.
     * @return
     */
    private Transform3D getView(int i){
        Point3d center = new Point3d(0.0, 0.0, 0.0);
        Point3d eyePos = new Point3d(center);
        if(i == 0){
                eyePos.z += 0;      // front
                eyePos.x -= 0.1;
                eyePos.y += 0;
        }
        if(i == 1) {   
                eyePos.z += 0;      // oben
                eyePos.x -= 0.5;
                eyePos.y += 0.7;     
        }
 
        Vector3d up = new Vector3d();
                up.y = 1;

        Transform3D viewTrans = new Transform3D();
        viewTrans.lookAt( eyePos, center, up );
        return viewTrans;
    }
    
    /**
     * Liefert die Uhr.
     * @return
     */
    public Clock getClock() {
    	return clock;
    }
    
    /**
     * Liefert das Canvas-Objekt.
     * @return
     */
    public Canvas3D getCanvas(){
        return canvas;
    }
    
    /**
     * Uhr schrittweise vorstellen.
     */
    private void spinClockForward() {
    	if (clock.isRunning()) {
    		clock.stop();	//Uhr anhalten
    		clockWasRunning = true;
    	}
    	
        clock.getHands().setMovementForward();
        handSpeedSteps();
    }
    
    /**
     * Uhr schrittweise zurueckstellen
     */
    private void spinClockBackward() {
    	if (clock.isRunning()) {
    		clock.stop();	//Uhr anhalten
    		clockWasRunning = true;
    	}
    	
        clock.getHands().setMovementBackward();
        handSpeedSteps();        
    }
    
    /**
     * Variiert die Geschwindigkeit, nach der die Uhrzeiger in die festgelegte
     * Drehrichtung versetzt werden, abhaengig von der Dauer des entsprechenden
     * Tastendrucks. 
     */
    private void handSpeedSteps() {
    	if (keyPressedCounter < 60)
        	clock.getHands().moveBothHands(6);
        else if (keyPressedCounter < 120)
        	clock.getHands().moveBothHands(12);
        else if (keyPressedCounter < 180)
        	clock.getHands().moveBothHands(24);
        else
        	clock.getHands().moveBothHands(36);
    }
    
    /**
     * Setzt das Geschwindigkeitslevel der Uhr in vier Stufen.
     * Zu Praesentationszwecken.
     */
    private void turboMode() {
    	if (clock.isRunning()) {
	    	switch(clock.getSpeedLevel()) {
	    	case 1:	clock.setSpeedLevel(2); break;
	    	case 2: clock.setSpeedLevel(3); break;
	    	case 3: clock.setSpeedLevel(4); break;
	    	case 4: clock.setSpeedLevel(1);
	    	}
    	}
    }
}
